#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
配置文件管理模块
负责管理任务配置和调度器设置
"""

import os
import json
import yaml
from typing import Dict, List, Any, Optional
from datetime import datetime
import logging

# 导入模块，支持相对导入和绝对导入
try:
    from .task_scheduler import ScheduledTask
except ImportError:
    # 如果直接运行文件，尝试绝对导入
    try:
        from task_scheduler import ScheduledTask
    except ImportError as e:
        print(f"导入错误: {e}", file=__import__('sys').stderr)
        print("请确保项目结构正确", file=__import__('sys').stderr)
        __import__('sys').exit(1)


class ConfigManager:
    """配置管理器"""

    def __init__(self, config_dir: str = "config", config_file: str = "scheduler.yaml"):
        """
        初始化配置管理器

        Args:
            config_dir: 配置目录
            config_file: 配置文件名
        """
        self.config_dir = config_dir
        self.config_file = os.path.join(config_dir, config_file)
        self.tasks_file = os.path.join(config_dir, "tasks.json")
        self.state_file = os.path.join(config_dir, "scheduler_state.json")

        # 确保配置目录存在
        os.makedirs(config_dir, exist_ok=True)

        # 默认配置
        self.default_config = {
            "scheduler": {
                "max_workers": 5,
                "log_file": "scheduler.log",
                "log_dir": "logs",  # 添加日志目录配置项
                "log_level": "INFO",
                "check_interval": 1,  # 秒
                "auto_save_interval": 60  # 秒
            },
            "service": {
                "service_name": "PyCronScheduler",
                "display_name": "Cron Python Scheduler",
                "description": "Windows定时任务调度服务，支持cron表达式"
            },
            "tasks": []
        }

        self.config = self._load_config()
        self.tasks_config = self._load_tasks_config()

    def _load_config(self) -> Dict[str, Any]:
        """加载主配置文件"""
        if not os.path.exists(self.config_file):
            # 创建默认配置文件
            self._save_config(self.default_config)
            return self.default_config.copy()

        try:
            # 首先尝试UTF-8编码
            with open(self.config_file, 'r', encoding='utf-8') as f:
                if self.config_file.endswith('.yaml') or self.config_file.endswith('.yml'):
                    config = yaml.safe_load(f) or {}
                else:
                    config = json.load(f)

            # 合并默认配置
            merged_config = self.default_config.copy()
            self._deep_merge(merged_config, config)

            return merged_config

        except UnicodeDecodeError:
            # 如果UTF-8失败，尝试GBK编码
            try:
                with open(self.config_file, 'r', encoding='gbk') as f:
                    if self.config_file.endswith('.yaml') or self.config_file.endswith('.yml'):
                        config = yaml.safe_load(f) or {}
                    else:
                        config = json.load(f)

                # 合并默认配置
                merged_config = self.default_config.copy()
                self._deep_merge(merged_config, config)

                return merged_config
            except Exception as e:
                logging.warning(f"加载配置文件失败 (尝试了UTF-8和GBK编码)，使用默认配置: {e}")
                return self.default_config.copy()
        except Exception as e:
            logging.warning(f"加载配置文件失败，使用默认配置: {e}")
            return self.default_config.copy()

    def _load_tasks_config(self) -> List[Dict[str, Any]]:
        """加载任务配置文件"""
        if not os.path.exists(self.tasks_file):
            return []

        try:
            # 首先尝试UTF-8编码
            with open(self.tasks_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
                return data.get("tasks", [])
        except UnicodeDecodeError:
            # 如果UTF-8失败，尝试GBK编码
            try:
                with open(self.tasks_file, 'r', encoding='gbk') as f:
                    data = json.load(f)
                    return data.get("tasks", [])
            except Exception as e:
                logging.warning(f"加载任务配置文件失败 (尝试了UTF-8和GBK编码): {e}")
                return []
        except Exception as e:
            logging.warning(f"加载任务配置文件失败: {e}")
            return []

    def _save_config(self, config: Dict[str, Any]):
        """保存主配置文件"""
        try:
            os.makedirs(os.path.dirname(self.config_file), exist_ok=True)

            with open(self.config_file, 'w', encoding='utf-8') as f:
                if self.config_file.endswith('.yaml') or self.config_file.endswith('.yml'):
                    yaml.dump(config, f, default_flow_style=False, allow_unicode=True, indent=2)
                else:
                    json.dump(config, f, indent=2, ensure_ascii=False)

        except Exception as e:
            logging.error(f"保存配置文件失败: {e}")

    def _save_tasks_config(self, tasks: List[Dict[str, Any]]):
        """保存任务配置文件"""
        try:
            data = {
                "tasks": tasks,
                "updated_at": datetime.now().isoformat()
            }

            with open(self.tasks_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)

        except Exception as e:
            logging.error(f"保存任务配置文件失败: {e}")

    def _deep_merge(self, base: Dict[str, Any], update: Dict[str, Any]):
        """深度合并字典"""
        for key, value in update.items():
            if key in base and isinstance(base[key], dict) and isinstance(value, dict):
                self._deep_merge(base[key], value)
            else:
                base[key] = value

    def get_scheduler_config(self) -> Dict[str, Any]:
        """获取调度器配置"""
        return self.config.get("scheduler", {})

    def get_service_config(self) -> Dict[str, Any]:
        """获取服务配置"""
        return self.config.get("service", {})

    def update_scheduler_config(self, updates: Dict[str, Any]):
        """更新调度器配置"""
        if "scheduler" not in self.config:
            self.config["scheduler"] = {}

        self.config["scheduler"].update(updates)
        self._save_config(self.config)
        logging.info("调度器配置已更新")

    def update_service_config(self, updates: Dict[str, Any]):
        """更新服务配置"""
        if "service" not in self.config:
            self.config["service"] = {}

        self.config["service"].update(updates)
        self._save_config(self.config)
        logging.info("服务配置已更新")

    def get_all_tasks_config(self) -> List[Dict[str, Any]]:
        """获取所有任务配置"""
        return self.tasks_config.copy()

    def get_task_config(self, task_id: str) -> Optional[Dict[str, Any]]:
        """获取指定任务配置"""
        for task_config in self.tasks_config:
            if task_config.get("task_id") == task_id:
                return task_config.copy()
        return None

    def add_task_config(self, task_config: Dict[str, Any]):
        """添加任务配置"""
        # 检查任务ID是否已存在
        for existing in self.tasks_config:
            if existing.get("task_id") == task_config.get("task_id"):
                raise ValueError(f"任务ID {task_config.get('task_id')} 已存在")

        # 添加时间戳
        task_config["created_at"] = datetime.now().isoformat()
        task_config["updated_at"] = datetime.now().isoformat()

        self.tasks_config.append(task_config)
        self._save_tasks_config(self.tasks_config)
        logging.info(f"任务配置已添加: {task_config.get('name')} ({task_config.get('task_id')})")

    def update_task_config(self, task_id: str, updates: Dict[str, Any]):
        """更新任务配置"""
        for i, task_config in enumerate(self.tasks_config):
            if task_config.get("task_id") == task_id:
                # 保留创建时间，更新修改时间
                updates["updated_at"] = datetime.now().isoformat()
                if "created_at" not in updates:
                    updates["created_at"] = task_config.get("created_at")

                self.tasks_config[i].update(updates)
                self._save_tasks_config(self.tasks_config)
                logging.info(f"任务配置已更新: {task_id}")
                return

        raise ValueError(f"任务ID {task_id} 不存在")

    def remove_task_config(self, task_id: str):
        """移除任务配置"""
        for i, task_config in enumerate(self.tasks_config):
            if task_config.get("task_id") == task_id:
                removed = self.tasks_config.pop(i)
                self._save_tasks_config(self.tasks_config)
                logging.info(f"任务配置已移除: {removed.get('name')} ({task_id})")
                return

        raise ValueError(f"任务ID {task_id} 不存在")

    def create_task_from_config(self, task_config: Dict[str, Any]) -> ScheduledTask:
        """从配置创建任务对象"""
        try:
            # 处理环境变量
            environment = task_config.get("environment", {})
            if isinstance(environment, str):
                # 如果是字符串，尝试解析为JSON
                try:
                    environment = json.loads(environment)
                except:
                    environment = {}

            task = ScheduledTask(
                task_id=task_config["task_id"],
                name=task_config["name"],
                cron_expression=task_config["cron_expression"],
                command=task_config["command"],
                enabled=task_config.get("enabled", True),
                description=task_config.get("description", ""),
                working_dir=task_config.get("working_dir", ""),
                environment=environment,
                timeout=task_config.get("timeout"),
                max_retries=task_config.get("max_retries", 0)
            )

            # 初始化下次运行时间
            if task.enabled:
                task.update_next_run_time()

            return task

        except KeyError as e:
            raise ValueError(f"任务配置缺少必需字段: {e}")
        except Exception as e:
            raise ValueError(f"创建任务失败: {e}")

    def load_all_tasks(self) -> List[ScheduledTask]:
        """加载所有任务对象"""
        tasks = []
        for task_config in self.tasks_config:
            try:
                task = self.create_task_from_config(task_config)
                tasks.append(task)
            except Exception as e:
                logging.error(f"加载任务 {task_config.get('task_id')} 失败: {e}")

        return tasks

    def save_task_to_config(self, task: ScheduledTask):
        """将任务对象保存到配置"""
        task_config = task.to_dict()

        # 查找是否已存在
        existing_index = None
        for i, config in enumerate(self.tasks_config):
            if config.get("task_id") == task.task_id:
                existing_index = i
                break

        if existing_index is not None:
            # 更新现有任务
            self.tasks_config[existing_index] = task_config
        else:
            # 添加新任务
            task_config["created_at"] = datetime.now().isoformat()
            self.tasks_config.append(task_config)

        task_config["updated_at"] = datetime.now().isoformat()
        self._save_tasks_config(self.tasks_config)

    def export_config(self, file_path: str):
        """导出完整配置"""
        try:
            full_config = {
                "config": self.config,
                "tasks": self.tasks_config,
                "exported_at": datetime.now().isoformat()
            }

            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(full_config, f, indent=2, ensure_ascii=False)

            logging.info(f"配置已导出到: {file_path}")

        except Exception as e:
            logging.error(f"导出配置失败: {e}")

    def import_config(self, file_path: str):
        """导入配置"""
        try:
            # 首先尝试UTF-8编码
            with open(file_path, 'r', encoding='utf-8') as f:
                data = json.load(f)

            if "config" in data:
                self.config = data["config"]
                self._save_config(self.config)

            if "tasks" in data:
                self.tasks_config = data["tasks"]
                self._save_tasks_config(self.tasks_config)

            logging.info(f"配置已从 {file_path} 导入")

        except UnicodeDecodeError:
            # 如果UTF-8失败，尝试GBK编码
            try:
                with open(file_path, 'r', encoding='gbk') as f:
                    data = json.load(f)

                if "config" in data:
                    self.config = data["config"]
                    self._save_config(self.config)

                if "tasks" in data:
                    self.tasks_config = data["tasks"]
                    self._save_tasks_config(self.tasks_config)

                logging.info(f"配置已从 {file_path} 导入 (使用GBK编码)")
            except Exception as e:
                logging.error(f"导入配置失败 (尝试了UTF-8和GBK编码): {e}")
                raise
        except Exception as e:
            logging.error(f"导入配置失败: {e}")
            raise

    def get_task_templates(self) -> List[Dict[str, Any]]:
        """获取任务模板"""
        templates = [
            {
                "name": "Python脚本任务",
                "description": "执行Python脚本",
                "cron_expression": "0 * * * * *",  # 每小时执行
                "command": "python script.py",
                "working_dir": "",
                "environment": {},
                "timeout": 300,
                "max_retries": 3
            },
            {
                "name": "系统命令任务",
                "description": "执行系统命令",
                "cron_expression": "0 9 * * * *",  # 每天上午9点
                "command": "echo 'Hello World'",
                "working_dir": "",
                "environment": {},
                "timeout": 60,
                "max_retries": 1
            },
            {
                "name": "备份任务",
                "description": "执行备份脚本",
                "cron_expression": "0 2 * * * *",  # 每天凌晨2点
                "command": "python backup.py",
                "working_dir": "/path/to/backup",
                "environment": {"BACKUP_DIR": "/backup"},
                "timeout": 3600,
                "max_retries": 2
            },
            {
                "name": "监控任务",
                "description": "系统监控脚本",
                "cron_expression": "*/30 * * * * *",  # 每30秒
                "command": "python monitor.py",
                "working_dir": "",
                "environment": {},
                "timeout": 30,
                "max_retries": 0
            }
        ]
        return templates

    def validate_task_config(self, task_config: Dict[str, Any]) -> List[str]:
        """验证任务配置"""
        errors = []

        # 检查必需字段
        required_fields = ["task_id", "name", "cron_expression", "command"]
        for field in required_fields:
            if not task_config.get(field):
                errors.append(f"缺少必需字段: {field}")
            elif not isinstance(task_config[field], str):
                errors.append(f"字段 {field} 必须是字符串类型")

        # 验证task_id格式
        if task_config.get("task_id"):
            task_id = task_config["task_id"]
            if not task_id.replace("_", "").replace("-", "").isalnum():
                errors.append("任务ID只能包含字母、数字、下划线和横线")

        # 验证cron表达式
        if task_config.get("cron_expression"):
            from .cron_parser import validate_cron_expression
            if not validate_cron_expression(task_config["cron_expression"]):
                errors.append("无效的cron表达式")

        # 验证数字字段
        if "timeout" in task_config and task_config["timeout"] is not None:
            if not isinstance(task_config["timeout"], int) or task_config["timeout"] <= 0:
                errors.append("超时时间必须是正整数")

        if "max_retries" in task_config and task_config["max_retries"] is not None:
            if not isinstance(task_config["max_retries"], int) or task_config["max_retries"] < 0:
                errors.append("最大重试次数必须是非负整数")

        return errors


